package org.ghost.http.okhttp;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import org.ghost.http.okhttp.builder.*;
import org.ghost.http.okhttp.callback.Callback;
import org.ghost.http.okhttp.https.HttpsUtils;
import org.ghost.http.okhttp.log.LoggerInterceptor;
import org.ghost.http.okhttp.request.RequestCall;

import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 15/8/17.
 */
public class OkHttpUtils {
    public static final long DEFAULT_MILLISECONDS = 10_000L;
    private OkHttpClient okHttpClient;
    private Executor executor;

    private enum Singleton {
        INSTANCE;
        private OkHttpUtils singleton;

        //JVM会保证此方法绝对只调用一次
        Singleton() {
            singleton = new OkHttpUtils();
        }

        public OkHttpUtils getInstance() {
            return singleton;
        }
    }

    private OkHttpUtils() {
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(new LoggerInterceptor(true))
                .connectTimeout(10000L, TimeUnit.MILLISECONDS)
                .readTimeout(10000L, TimeUnit.MILLISECONDS)
                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
                .build();
        executor = Executors.newCachedThreadPool();
    }

    public static OkHttpUtils getInstance() {
        return Singleton.INSTANCE.getInstance();
    }


    public Executor getDelivery() {
        return this.executor;
    }

    public OkHttpClient getOkHttpClient() {
        return okHttpClient;
    }

    public GetBuilder get() {
        return new GetBuilder();
    }

    public PostStringBuilder postString() {
        return new PostStringBuilder();
    }

    public PostFileBuilder postFile() {
        return new PostFileBuilder();
    }

    public PostFormBuilder post() {
        return new PostFormBuilder();
    }

    public OtherRequestBuilder put() {
        return new OtherRequestBuilder(METHOD.PUT);
    }

    public HeadBuilder head() {
        return new HeadBuilder();
    }

    public OtherRequestBuilder delete() {
        return new OtherRequestBuilder(METHOD.DELETE);
    }

    public OtherRequestBuilder patch() {
        return new OtherRequestBuilder(METHOD.PATCH);
    }

    public void execute(final RequestCall requestCall, okhttp3.Callback callback) {
        requestCall.getCall().enqueue(callback);
    }

    public void execute(final RequestCall requestCall, Callback callback) {
        if (callback == null) {
            callback = Callback.CALLBACK_DEFAULT;
        }
        final Callback finalCallback = callback;
        final int id = requestCall.getOkHttpRequest().getId();

        requestCall.getCall().enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                sendFailResultCallback(call, e, finalCallback, id);
            }

            @Override
            public void onResponse(final Call call, final Response response) {
                try {
                    if (call.isCanceled()) {
                        sendFailResultCallback(call, new IOException("Canceled!"), finalCallback, id);
                        return;
                    }

                    if (!finalCallback.validateReponse(response, id)) {
                        sendFailResultCallback(call, new IOException("request failed , reponse's code is : " + response.code()), finalCallback, id);
                        return;
                    }

                    Object o = finalCallback.parseNetworkResponse(response, id);
                    sendSuccessResultCallback(o, finalCallback, id);
                } catch (Exception e) {
                    sendFailResultCallback(call, e, finalCallback, id);
                } finally {
                    if (response.body() != null) {
                        response.body().close();
                    }
                }
            }
        });
    }


    public void sendFailResultCallback(final Call call, final Exception e, final Callback callback, final int id) {
        if (callback == null) return;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                callback.onError(call, e, id);
                callback.onAfter(id);
            }
        });
    }

    public void sendSuccessResultCallback(final Object object, final Callback callback, final int id) {
        if (callback == null) return;
        executor.execute(new Runnable() {
            @Override
            public void run() {
                callback.onResponse(object, id);
                callback.onAfter(id);
            }
        });
    }

    public void cancelTag(Object tag) {
        for (Call call : okHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : okHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    public static class METHOD {
        public static final String HEAD = "HEAD";
        public static final String DELETE = "DELETE";
        public static final String PUT = "PUT";
        public static final String PATCH = "PATCH";
    }
}

